from django.http import StreamingHttpResponse
from rest_framework.views import APIView
from rest_framework import status
from rest_framework.permissions import IsAuthenticated
from ..services.fine_tuning_service import FineTuningService
from .global_responses import BaseResponse
import json
import logging
import hashlib
import time
from django.core.cache import cache
from queue import Queue
import threading
import uuid
from django.shortcuts import get_object_or_404
from ..models import Note

logger = logging.getLogger(__name__)

# 请求速率限制
MAX_REQUESTS_PER_MINUTE = 20
# 全局请求队列和队列锁
request_queue = Queue(maxsize=30)
queue_lock = threading.Lock()

class FineTuningNoteOptimizationView(APIView):
    """
    提供笔记优化API接口，使用硅基流动微调后的模型进行笔记补充和知识扩展
    
    专业能力：
    1. 学科专家角色扮演 - 根据笔记学科自动以相应领域专家身份提供专业指导
    2. 知识深度拓展 - 对重要概念进行多维度、深层次的解析与拓展
    3. 考点精准标注 - 使用⭐标识突出考试重点和易错点，帮助备考
    4. 学术精确表达 - 确保概念定义、公式推导和理论阐述的学术准确性
    5. 应用案例补充 - 为抽象理论配备实际应用场景和典型案例
    
    支持学科：
    数学、物理、计算机科学、化学、生物学、经济学、文学、历史学、哲学、
    心理学、医学、法学、工程学、语言学、马克思主义理论等多个大学学科
    
    API通过流式响应实时返回优化结果，支持缓存以提高性能
    """
    permission_classes = [IsAuthenticated]
    
    def _cache_result(self, note_content, result, task_type="enrich", subject_area=None, expiry=None):
        """
        缓存处理结果，以便后续相同请求可以快速返回
        
        参数:
        - note_content: 原始笔记内容
        - result: 处理结果
        - task_type: 任务类型
        - subject_area: 学科领域
        - expiry: 缓存过期时间
        """
        if not note_content or not result:
            return
        
        try:
            # 生成缓存键
            cache_key = self._generate_cache_key(note_content, task_type, subject_area)
            
            # 准备缓存内容
            cache_data = {
                "optimized_content": result.get("content", ""),
                "task_type": task_type,
                "from_cache": True
            }
            
            # 如果有AI内容，也缓存
            if "ai_content" in result and result["ai_content"]:
                cache_data["ai_content"] = result["ai_content"]
            
            # 如果有学科领域，添加到缓存数据
            if subject_area:
                cache_data["subject_area"] = subject_area
            
            # 设置缓存
            if expiry:
                cache.set(cache_key, cache_data, expiry.total_seconds())
            else:
                # 默认3小时
                cache.set(cache_key, cache_data, 60 * 60 * 3)
            
            logger.info(f"结果已缓存，缓存键: {cache_key[:10]}...")
        except Exception as e:
            logger.error(f"缓存结果时出错: {str(e)}")
    
    def post(self, request):
        """
        处理POST请求，调用TestOptimizationService服务优化笔记
        
        支持两种方式：
        1. 通过note_id获取笔记内容
        2. 直接传入note_content
        """
        # 从请求中获取参数
        try:
            data = json.loads(request.body)
            note_content = data.get('note_content', '')
            note_id = data.get('note_id')
            task_type = data.get('task_type', 'enrich')
            subject_area = data.get('subject_area', None)
            stream = data.get('stream', True)  # 默认使用流式输出
            note_title = data.get('note_title', '')  # 新增：获取笔记标题
            
            # 如果提供了笔记ID，优先使用笔记ID获取内容
            if note_id:
                try:
                    # 获取用户笔记
                    note = get_object_or_404(Note, id=note_id, user=request.user)
                    note_content = note.content
                    
                    # 同时获取笔记标题（如果前端未传入）
                    if not note_title and hasattr(note, 'title'):
                        note_title = note.title
                    
                    # 记录日志
                    logger.info(f"通过笔记ID获取内容: {note_id}, 标题: {note_title}, 内容长度: {len(note_content)}字符")
                    
                    # 如果内容为空，返回错误
                    if not note_content:
                        return BaseResponse.error("该笔记内容为空")
                except Exception as e:
                    logger.error(f"获取笔记内容失败: {str(e)}")
                    return BaseResponse.error(f"获取笔记内容失败: {str(e)}")
            
            # 验证必须参数
            if not note_content:
                return BaseResponse.error("笔记内容不能为空，请提供note_content或有效的note_id")
                
            # 创建服务实例 
            service = FineTuningService()
            
            # 记录请求信息
            request_id = uuid.uuid4().hex
            is_long_content = len(note_content) > 6000
            
            logger.info(f"接收到笔记优化请求 [ID:{request_id}], 任务类型: {task_type}, 学科: {subject_area}, 流式: {stream}, 标题: {note_title}, 内容长度: {len(note_content)}字符")
            
            # 处理流式请求
            if stream:
                # 获取流式生成器
                content_generator = service.optimize_note(
                    note_content=note_content,
                    task_type=task_type,
                    subject_area=subject_area,
                    stream=True,
                    note_title=note_title  # 新增：传入笔记标题
                )
                
                # 如果是长文本，包装生成器以提供状态提示
                if is_long_content:
                    content_generator = self._wrap_generator_with_status(content_generator, len(note_content))
                
                # 创建流式响应
                streaming_response = self._handle_streaming_response(content_generator, request_id)
                
                # 设置响应头，避免缓存并允许跨域
                response = StreamingHttpResponse(
                    streaming_content=streaming_response,
                    content_type='text/event-stream'
                )
                response['Cache-Control'] = 'no-cache'
                response['X-Accel-Buffering'] = 'no'
                response['Access-Control-Allow-Origin'] = '*'
                
                return response
            else:
                # 非流式响应 - 但我们仍使用流式API处理后返回完整内容
                try:
                    # 获取流式生成器
                    content_generator = service.optimize_note(
                        note_content=note_content,
                        task_type=task_type,
                        subject_area=subject_area,
                        stream=True,  # 内部仍使用流式API
                        note_title=note_title  # 新增：传入笔记标题
                    )
                    
                    # 收集所有生成内容
                    complete_content = None
                    ai_comments = None
                    error = None
                    
                    for content in content_generator:
                        if "error" in content:
                            error = content["error"]
                            break
                        elif "complete" in content and content["complete"]:
                            complete_content = content.get("optimized_content", "")
                            ai_comments = content.get("ai_comments", "")
                    
                    if error:
                        logger.error(f"非流式笔记优化请求 [ID:{request_id}] 失败: {error}")
                        return BaseResponse.error(error, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
                    
                    # 确保我们有内容
                    if not complete_content:
                        logger.warning(f"非流式笔记优化请求 [ID:{request_id}] 未返回内容")
                        return BaseResponse.error("服务未返回有效内容", status=status.HTTP_500_INTERNAL_SERVER_ERROR)
                    
                    # 构建响应
                    response_data = {
                        "optimized_content": complete_content
                    }
                    
                    if ai_comments:
                        response_data["ai_comments"] = ai_comments
                    
                    # 如果请求来自笔记ID，添加笔记信息到响应
                    if note_id:
                        response_data["note_id"] = note_id
                        
                    logger.info(f"非流式笔记优化请求 [ID:{request_id}] 完成，内容长度: {len(complete_content)}")
                    return BaseResponse.success(response_data)
                
                except Exception as e:
                    logger.exception(f"非流式笔记优化请求 [ID:{request_id}] 处理失败: {str(e)}")
                    return BaseResponse.error(f"处理请求失败: {str(e)}", status=status.HTTP_500_INTERNAL_SERVER_ERROR)
                
        except json.JSONDecodeError:
            return BaseResponse.error("无效的JSON格式")
        except Exception as e:
            logger.exception(f"处理笔记优化请求失败: {str(e)}")
            return BaseResponse.error(f"处理请求失败: {str(e)}", status=status.HTTP_500_INTERNAL_SERVER_ERROR)

    def _handle_streaming_response(self, content_generator, request_id):
        """
        处理流式响应
        
        参数:
        - content_generator: 内容生成器
        - request_id: 请求ID，用于日志跟踪
        
        返回:
        - 一个生成器，逐步yield格式化的SSE消息
        """
        # 用于追踪最终的完整响应
        has_yielded_completion = False
        has_sent_final_response = False
        last_send_time = time.time()
        chunk_buffer = ""  # 用于缓存小的数据块
        
        try:
            # 处理来自服务的流式内容
            for content in content_generator:
                # 检查是否有错误
                if "error" in content:
                    error_msg = content["error"]
                    logger.error(f"流式笔记优化请求 [ID:{request_id}] 发生错误: {error_msg}")
                    yield f"data: {json.dumps({'error': error_msg})}\n\n"
                    break
                
                # 处理状态消息
                if "status" in content and content["status"]:
                    status_data = {
                        "status": True,
                        "message": content.get("message", ""),
                        "content_length": content.get("content_length", 0)
                    }
                    yield f"data: {json.dumps(status_data)}\n\n"
                    continue
                
                # 处理完整响应
                if "complete" in content and content["complete"]:
                    if not has_yielded_completion:
                        has_yielded_completion = True
                        optimized_content = content.get("optimized_content", "")
                        ai_comments = content.get("ai_comments", "")
                        
                        # 确保有内容
                        if optimized_content:
                            # 构建完整响应数据
                            complete_data = {
                                "complete": True,
                                "optimized_content": optimized_content
                            }
                            if ai_comments:
                                complete_data["ai_comments"] = ai_comments
                            
                            logger.info(f"流式笔记优化请求 [ID:{request_id}] 完成，内容长度: {len(optimized_content)}")
                            yield f"data: {json.dumps(complete_data)}\n\n"
                            has_sent_final_response = True
                
                # 处理进度信息
                elif "is_progress" in content and content["is_progress"]:
                    # 这是处理进度信息，直接发送给客户端
                    progress_data = {
                        "progress": True,
                        "message": content.get("chunk", "")
                    }
                    yield f"data: {json.dumps(progress_data)}\n\n"
                    last_send_time = time.time()
                
                # 处理分段内容
                elif "is_segment" in content and content["is_segment"]:
                    # 处理特定段落的内容
                    segment_num = content.get("segment_num", 0)
                    chunk_content = content.get("chunk", "")
                    
                    if chunk_content:
                        segment_data = {
                            "segment": True,
                            "segment_num": segment_num,
                            "content": chunk_content
                        }
                        yield f"data: {json.dumps(segment_data)}\n\n"
                        last_send_time = time.time()
                
                # 处理分块内容
                elif "chunk" in content:
                    current_time = time.time()
                    time_since_last_send = current_time - last_send_time
                    chunk_content = content["chunk"]
                    
                    # 如果时间间隔短且内容少，缓存内容
                    if time_since_last_send < 0.15 and len(chunk_content) < 50:
                        chunk_buffer += chunk_content
                    else:
                        # 如果有缓存内容，合并发送
                        if chunk_buffer:
                            chunk_content = chunk_buffer + chunk_content
                            chunk_buffer = ""
                        
                        # 发送数据块
                        if chunk_content:
                            chunk_data = {
                                "chunk": chunk_content,
                                "optimized_content": content.get("optimized_content", "")
                            }
                            yield f"data: {json.dumps(chunk_data)}\n\n"
                            last_send_time = current_time
            
            # 发送任何剩余的缓存内容
            if chunk_buffer and not has_sent_final_response:
                chunk_data = {"chunk": chunk_buffer}
                yield f"data: {json.dumps(chunk_data)}\n\n"
            
            # 如果没有发送过完整响应，发送一个带有空内容的完整响应
            if not has_sent_final_response:
                logger.warning(f"流式笔记优化请求 [ID:{request_id}] 未能提供完整响应")
                yield f"data: {json.dumps({'complete': True, 'optimized_content': ''})}\n\n"
                
        except GeneratorExit:
            # 客户端断开连接
            logger.info(f"流式笔记优化请求 [ID:{request_id}] 客户端断开连接")
        except Exception as e:
            # 处理过程中出现异常
            logger.exception(f"流式笔记优化请求 [ID:{request_id}] 处理异常: {str(e)}")
            yield f"data: {json.dumps({'error': str(e)})}\n\n"
        finally:
            logger.info(f"流式笔记优化请求 [ID:{request_id}] 响应处理完成")

    def _generate_cache_key(self, content, task_type, subject_area):
        """生成缓存键"""
        # 使用内容哈希、任务类型和学科作为缓存键
        content_hash = hashlib.md5(content.encode('utf-8')).hexdigest()
        return f"test_note_opt:{content_hash}:{task_type}:{subject_area or 'none'}"

    def _wrap_generator_with_status(self, generator, content_length):
        """
        包装生成器，为长文本处理添加状态提示
        
        参数:
        - generator: 原始内容生成器
        - content_length: 内容长度
        
        返回:
        - 添加状态提示的生成器
        """
        # 首先发送一个状态提示
        status_message = "您的笔记内容较长，系统将采用分段处理。请耐心等待，处理过程中您将看到实时进度。"
        yield {"status": True, "message": status_message, "content_length": content_length}
        
        # 然后传递原始生成器的内容
        for content in generator:
            yield content
